#ifndef PROJECTMANAGER_H
#define PROJECTMANAGER_H

#include "globals.h"
#include <wx/event.h>
#include <wx/dynarray.h>
#include <wx/hashmap.h>
#include <wx/treectrl.h>
#include "M_Code_exception.h"
#include "M_Code_treectrl.h"
#include "manager.h"

class M_Code_Project;
class EditorBase;
class ProjectFile;
class FilesGroupsAndMasks;
class M_Code_Workspace;
class M_Code_AuiNotebook;

WX_DEFINE_ARRAY( M_Code_Project*, ProjectsArray );
WX_DECLARE_HASH_MAP( M_Code_Project*, ProjectsArray*, wxPointerHash, wxPointerEqual, DepsMap );

class cbProjectManagerUI {
  public:
    virtual ~cbProjectManagerUI() {}
    virtual M_Code_AuiNotebook* GetNotebook() = 0;
    virtual M_Code_TreeCtrl* GetTree() = 0;
    virtual void RebuildTree() = 0;
    virtual void FreezeTree() = 0;
    virtual void UnfreezeTree( bool force = false ) = 0;
    virtual wxTreeItemId GetTreeSelection() = 0;
    virtual void ShowFileInTree( ProjectFile &projectFile ) = 0;
    virtual void UpdateActiveProject( M_Code_Project *oldProject, M_Code_Project *newProject, bool refresh ) = 0;
    virtual void RemoveProject( M_Code_Project *project ) = 0;
    virtual void BeginLoadingWorkspace() = 0;
    virtual void CloseWorkspace() = 0;
    virtual void FinishLoadingProject( M_Code_Project *project, bool newAddition, FilesGroupsAndMasks* fileGroups ) = 0;
    virtual void FinishLoadingWorkspace( M_Code_Project *activeProject, const wxString &workspaceTitle ) = 0;
    virtual bool QueryCloseAllProjects() = 0;
    virtual bool QueryCloseProject( M_Code_Project *proj, bool dontsavefiles = false ) = 0;
    virtual bool QueryCloseWorkspace() = 0;
    virtual int AskForBuildTargetIndex( M_Code_Project* project = nullptr ) = 0;
    virtual wxArrayInt AskForMultiBuildTargetIndex( M_Code_Project* project = nullptr ) = 0;
    virtual void ConfigureProjectDependencies( M_Code_Project* base = nullptr ) = 0;
    virtual void SwitchToProjectsPage() = 0;
};



class ProjectManager : public Mgr<ProjectManager>, public wxEvtHandler {
    static bool s_CanShutdown;
  public:
    friend class Mgr<ProjectManager>;
    friend class Manager;
    cbProjectManagerUI& GetUI() { return *m_ui; }
    void SetUI( cbProjectManagerUI *ui );

    const FilesGroupsAndMasks* GetFilesGroupsAndMasks() const { return m_pFileGroups; }
    FilesGroupsAndMasks* GetFilesGroupsAndMasks() { return m_pFileGroups; }
    static bool CanShutdown() { return s_CanShutdown; }
    wxString GetDefaultPath();
    void SetDefaultPath( const wxString& path );
    bool IsProjectStillOpen( M_Code_Project* project );
    M_Code_Project* GetActiveProject() { return m_pActiveProject; }
    ProjectsArray* GetProjects() { return m_pProjects; }
    M_Code_Project* IsOpen( const wxString& filename );
    void SetProject( M_Code_Project* project, bool refresh = true );
    M_Code_Project* LoadProject( const wxString& filename, bool activateIt = true );
    void ReloadProject( M_Code_Project *project );
    bool SaveProject( M_Code_Project* project );
    bool SaveProjectAs( M_Code_Project* project );
    bool SaveActiveProject();
    bool SaveActiveProjectAs();
    bool SaveAllProjects();
    bool CloseProject( M_Code_Project* project, bool dontsave = false, bool refresh = true );
    bool CloseActiveProject( bool dontsave = false );
    bool CloseAllProjects( bool dontsave = false );
    M_Code_Project* NewProject( const wxString& filename = wxEmptyString );
    int AddFileToProject( const wxString& filename, M_Code_Project* project = nullptr, int target = -1 );
    int AddFileToProject( const wxString& filename, M_Code_Project* project, wxArrayInt& targets );
    int AddMultipleFilesToProject( const wxArrayString& filelist, M_Code_Project* project, int target = -1 );
    int AddMultipleFilesToProject( const wxArrayString& filelist, M_Code_Project* project, wxArrayInt& targets );
    void RemoveFileFromProject( ProjectFile* pfile, M_Code_Project* project );
    bool LoadWorkspace( const wxString& filename = DEFAULT_WORKSPACE );
    bool SaveWorkspace();
    bool SaveWorkspaceAs( const wxString& filename );
    bool CloseWorkspace();
    bool IsLoadingProject();
    bool IsLoadingWorkspace();
    bool IsLoading();
    bool IsClosingProject();
    bool IsClosingWorkspace();
    bool IsLoadingOrClosing();
    static bool IsBusy();
    M_Code_Workspace* GetWorkspace();
    bool AddProjectDependency( M_Code_Project* base, M_Code_Project* dependsOn );
    void RemoveProjectDependency( M_Code_Project* base, M_Code_Project* doesNotDependOn );
    void ClearProjectDependencies( M_Code_Project* base );
    void RemoveProjectFromAllDependencies( M_Code_Project* base );
    const ProjectsArray* GetDependenciesForProject( M_Code_Project* base );
    bool CausesCircularDependency( M_Code_Project* base, M_Code_Project* dependsOn );
    void WorkspaceChanged();
    bool BeginLoadingProject();
    void EndLoadingProject( M_Code_Project* project );
    bool BeginLoadingWorkspace();
    void EndLoadingWorkspace();

    ProjectManager& operator=( M_Code_unused const ProjectManager& rhs ) {
      m_Throw( _T( "Can't assign a ProjectManager* !!!" ) );
      return *this;
    }

    void SetIsRunning( M_Code_Plugin *plugin );
    M_Code_Plugin* GetIsRunning() const;
    M_Code_Project* FindProjectForFile( const wxString& file, ProjectFile **resultFile,
                                        bool isRelative, bool isUnixFilename );

  private:
    ProjectManager( M_Code_unused const ProjectManager& rhs );
    ProjectManager();
    ~ProjectManager();
    void OnAppDoneStartup( CodeBlocksEvent& event );
    int  DoAddFileToProject( const wxString& filename, M_Code_Project* project, wxArrayInt& targets );

    cbProjectManagerUI *m_ui;
    M_Code_Project* m_pActiveProject;
    M_Code_Project* m_pProjectToActivate;
    ProjectsArray* m_pProjects;
    DepsMap m_ProjectDeps;
    M_Code_Workspace* m_pWorkspace;
    FilesGroupsAndMasks* m_pFileGroups;
    bool m_IsLoadingProject;
    bool m_IsLoadingWorkspace;
    bool m_IsClosingProject;
    bool m_IsClosingWorkspace;
    wxString m_InitialDir;
    bool m_CanSendWorkspaceChanged;
    M_Code_Plugin* m_RunningPlugin;
    DECLARE_EVENT_TABLE()
};

#endif
